<!--
 * @Author: quit
 * @Date: 2021-12-23 10:52:05
 * @LastEditTime: 2022-01-15 11:28:28
 * @Descripttion: 画布,主显示区域
-->
<template>
    <!-- 组件容器 -->
    <div id="topo-main-container">
        <el-card>
            <div class="layer-container">
                <!-- 画布显示区域 -->
                <div
                    id="surface-edit-layer"
                    tabindex="0"
                    class="topo-layer"
                    :class="{ 'topo-layer-view-selected': selectedIsLayer }"
                    :style="layerStyle"
                    @click="onLayerClick($event)"
                    @mouseup="onLayerMouseup($event)"
                    @mousemove="onLayerMousemove($event)"
                    @mousedown="onLayerMousedown($event)"
                    @keyup.delete="removeItem()"
                    @dragover.prevent
                    @drop="onDrop"
                    @keydown.ctrl.67.stop="copyItem"
                    @keydown.ctrl.86.stop="pasteItem"
                    @keydown.ctrl.90.stop="undo"
                    @keydown.ctrl.89.stop="redo"
                >
                    <!-- 使用configData进行循环渲染item实例 -->
                    <template v-for="(component, index) in configData.components">
                        <!-- item实例 -->
                        <div
                            :key="component.identifier"
                            tabindex="0"
                            class="topo-layer-view"
                            :class="{
                                'topo-layer-view-selected': selectedComponentMap[component.identifier] == undefined ? false : true
                            }"
                            :style="{
                                left: component.style.position.x + 'px',
                                top: component.style.position.y + 'px',
                                width: component.style.position.w + 'px',
                                height: component.style.position.h + 'px',
                                'background-color': component.style.backColor,
                                zIndex: component.style.zIndex,
                                borderWidth: component.style.borderWidth + 'px',
                                borderStyle: component.style.borderStyle,
                                borderColor: component.style.borderColor,
                                transform: component.style.transform ? `rotate(${component.style.transform}deg)` : 'rotate(0deg)'
                            }"
                            @click.stop="clickComponent(index, component, $event)"
                            @mousedown.stop="controlMousedown(component, $event, index)"
                            @keyup.delete="removeItem()"
                            @keydown.up.exact.prevent="moveItems('up')"
                            @keydown.right.exact.prevent="moveItems('right')"
                            @keydown.down.exact.prevent="moveItems('down')"
                            @keydown.left.exact.prevent="moveItems('left')"
                            @keydown.ctrl.67.stop="copyItem"
                            @keydown.ctrl.86.stop="pasteItem"
                            @keydown.ctrl.90.stop="undo"
                            @keydown.ctrl.89.stop="redo"
                        >
                            <!-- 动态绑定不同组件效果 -->
                            <component
                                :is="parseView(component)"
                                :ref="'comp' + index"
                                :detail="component"
                                :edit-mode="true"
                                :selected="selectedComponentMap[component.identifier] ? true : false"
                            />
                            <!-- 大小调整角标 -->
                            <div
                                v-show="selectedComponentMap[component.identifier]"
                                class="resize-left-top"
                                @mousedown.stop="resizeMousedown(component, $event, index, 'resize-lt')"
                            />
                            <div
                                v-show="selectedComponentMap[component.identifier]"
                                class="resize-left-center"
                                @mousedown.stop="resizeMousedown(component, $event, index, 'resize-lc')"
                            />
                            <div
                                v-show="selectedComponentMap[component.identifier]"
                                class="resize-left-bottom"
                                @mousedown.stop="resizeMousedown(component, $event, index, 'resize-lb')"
                            />
                            <div
                                v-show="selectedComponentMap[component.identifier]"
                                class="resize-right-top"
                                @mousedown.stop="resizeMousedown(component, $event, index, 'resize-rt')"
                            />
                            <div
                                v-show="selectedComponentMap[component.identifier]"
                                class="resize-right-center"
                                @mousedown.stop="resizeMousedown(component, $event, index, 'resize-rc')"
                            />
                            <div
                                v-show="selectedComponentMap[component.identifier]"
                                class="resize-right-bottom"
                                @mousedown.stop="resizeMousedown(component, $event, index, 'resize-rb')"
                            />
                            <div
                                v-show="selectedComponentMap[component.identifier]"
                                class="resize-center-top"
                                @mousedown.stop="resizeMousedown(component, $event, index, 'resize-ct')"
                            />
                            <div
                                v-show="selectedComponentMap[component.identifier]"
                                class="resize-center-bottom"
                                @mousedown.stop="resizeMousedown(component, $event, index, 'resize-cb')"
                            />
                        </div>
                    </template>
                    <!-- 框选盒子 -->
                    <div
                        class="topo-frame-selection"
                        :style="{
                            width: frameSelectionDiv.width + 'px',
                            height: frameSelectionDiv.height + 'px',
                            top: frameSelectionDiv.top + 'px',
                            left: frameSelectionDiv.left + 'px'
                        }"
                    />
                </div>
            </div>
        </el-card>
    </div>
</template>

<script>
import { mapState, mapMutations, mapActions } from 'vuex'
// ToPo 工具组件导入入口文件
import TopoBase from '../ToPoBase/index.vue'
import ToPoMatch from './ToPoMatched/index'
import { deepCopy } from '@/utils/index'
import { checkByRectCollisionDetection } from '@/utils/ToPo'

export default {
    name: 'TopoMain',
    components: {},
    extends: TopoBase,
    props: [],
    data() {
        return {
            moveItem: {
                startX: 0,
                startY: 0
            }, // 移动组件 相关变量
            resizeItem: {
                startPx: 0,
                startPy: 0,
                x: 0,
                y: 0,
                w: 0,
                h: 0
            }, // resize组件 相关变量
            frameSelectionDiv: {
                width: 0,
                height: 0,
                top: 10,
                left: 10,
                startX: 0,
                startY: 0,
                startPageX: 0,
                startPageY: 0
            }, // 框选盒子的操作相关变量
            flag: '', // 当前操作标志位
            curControl: null,
            curIndex: -1
        }
    },
    computed: {
        ...mapState({
            selectedComponents: state => state.ToPo.selectedComponents,
            selectedComponentMap: state => state.ToPo.selectedComponentMap,
            configData: state => state.ToPo.topoData,
            selectedIsLayer: state => state.ToPo.selectedIsLayer,
            copySrcItems: state => state.ToPo.copySrcItems,
            copyCount: state => state.ToPo.copyCount,
            selectedValue: state => state.ToPo.selectedValue
        }),
        // 画板大小及属性
        layerStyle: function() {
            let scale = this.selectedValue / 100
            let styles = [`transform:scale(${scale})`]
            if (this.configData.layer.backColor) {
                styles.push(`background-color: ${this.configData.layer.backColor}`)
            }
            if (this.configData.layer.backgroundImage) {
                styles.push(`background-image: url("${this.configData.layer.backgroundImage}")`)
            }
            if (this.configData.layer.width > 0) {
                styles.push(`width: ${this.configData.layer.width}px`)
            }
            if (this.configData.layer.height > 0) {
                styles.push(`height: ${this.configData.layer.height}px`)
            }
            let style = styles.join(';')
            return style
        }
    },
    mounted() {
        this.loadDefaultTopoData()
    },
    methods: {
        ...mapMutations('ToPo', [
            'setSelectedComponent',
            'addSelectedComponent',
            'removeSelectedComponent',
            'clearSelectedComponent',
            'setLayerSelected',
            'setCopySrcItems',
            'increaseCopyCount',
            'execute',
            'undo',
            'redo'
        ]),
        ...mapActions('ToPo', ['loadDefaultTopoData']),
        controlMousedown(component, event, index) {
            if (event.ctrlKey) {
                return
            }
            this.flag = 'move'
            this.curControl = component
            this.clickItem(component, index)
            this.moveItem.startX = event.pageX
            this.moveItem.startY = event.pageY
            // 记录初始信息--move
            for (let key in this.selectedComponentMap) {
                let component = this.selectedComponentMap[key]
                component.style.temp = {}
                component.style.temp.position = {}
                component.style.temp.position.x = component.style.position.x
                component.style.temp.position.y = component.style.position.y
            }
        },
        resizeMousedown(component, $event, index, flag) {
            // resize-鼠标按下事件
            this.flag = flag
            this.curControl = component
            this.curIndex = index
            this.clickItem(component, index)
            // let dom = event.currentTarget
            this.resizeItem.startPx = event.pageX
            this.resizeItem.startPy = event.pageY
            // 记录初始信息-resize
            this.resizeItem.x = this.curControl.style.position.x
            this.resizeItem.y = this.curControl.style.position.y
            this.resizeItem.w = this.curControl.style.position.w
            this.resizeItem.h = this.curControl.style.position.h
        },
        onLayerMousemove(event) {
            if (event.which !== 1) {
                this.flag = ''
                return
            }
            if (this.flag === '') return
            if (this.flag.startsWith('resize')) {
                let dx = event.pageX - this.resizeItem.startPx
                let dy = event.pageY - this.resizeItem.startPy
                switch (this.flag) {
                    case 'resize-lt':
                        this.curControl.style.position.x = this.resizeItem.x + dx
                        this.curControl.style.position.y = this.resizeItem.y + dy
                        dx = -dx
                        dy = -dy
                        break
                    case 'resize-lc':
                        this.curControl.style.position.x = this.resizeItem.x + dx
                        dy = 0
                        dx = -dx
                        break
                    case 'resize-lb':
                        this.curControl.style.position.x = this.resizeItem.x + dx
                        dx = -dx
                        break
                    case 'resize-rt':
                        this.curControl.style.position.y = this.resizeItem.y + dy
                        dy = -dy
                        break
                    case 'resize-rc':
                        dy = 0
                        break
                    case 'resize-rb':
                        break
                    case 'resize-ct':
                        this.curControl.style.position.y = this.resizeItem.y + dy
                        dx = 0
                        dy = -dy
                        break
                    case 'resize-cb':
                        dx = 0
                        break
                }
                if (this.resizeItem.w + dx > 20) {
                    this.curControl.style.position.w = this.resizeItem.w + dx
                }
                if (this.resizeItem.h + dy > 20) {
                    this.curControl.style.position.h = this.resizeItem.h + dy
                }
                // canvas组件需要重新渲染
                // DOM 还没有更新
                this.$nextTick(function() {
                    // DOM 更新了
                    let a = this.$refs['comp' + this.curIndex][0]
                    a.onResize()
                })
            } else if (this.flag === 'move') {
                // 移动组件
                let dx = event.pageX - this.moveItem.startX
                let dy = event.pageY - this.moveItem.startY
                for (let key in this.selectedComponentMap) {
                    let component = this.selectedComponentMap[key]
                    component.style.position.x = component.style.temp.position.x + dx
                    component.style.position.y = component.style.temp.position.y + dy
                }
            } else if (this.flag === 'frame_selection') {
                this.onFrameSelection(event)
            }
        },

        // 画布区鼠标抬起事件
        onLayerMouseup(event) {
            // startsWith() JS内置方法用于查找当前字段是以检查内容开始返回一个布尔值
            if (this.flag.startsWith('resize')) {
                let a = this.$refs['comp' + this.curIndex][0]
                // ？ onResize() 未知定义
                a.onResize()
                console.log(a)
            } else if (this.flag === 'frame_selection') {
                // frame_selection 框选操作
                // 由于和onLayerClick冲突，这里模拟下点击空白区域
                this.onFrameSelection(event)
                this.frameSelectionDiv.width = 0
                this.frameSelectionDiv.height = 0
                this.frameSelectionDiv.top = 0
                this.frameSelectionDiv.left = 0
            } else if (this.flag === 'move') {
                // 鼠标move只是方便用户预览，真正执行应该用命令，所以要先恢复
                let dx = event.pageX - this.moveItem.startX
                let dy = event.pageY - this.moveItem.startY
                for (let key in this.selectedComponentMap) {
                    let component = this.selectedComponentMap[key]
                    component.style.position.x = component.style.position.x - dx
                    component.style.position.y = component.style.position.y - dy
                }
                this.execute({
                    op: 'move',
                    dx: dx,
                    dy: dy,
                    items: this.selectedComponentMap
                })
            }
            this.flag = ''
        },
        onLayerMousedown($event) {
            // 画布鼠标按下 记录当前
            this.flag = 'frame_selection'
            this.frameSelectionDiv.startX = event.offsetX
            this.frameSelectionDiv.startY = event.offsetY
            this.frameSelectionDiv.startPageX = event.pageX
            this.frameSelectionDiv.startPageY = event.pageY
        },

        // main区域点击事件
        onLayerClick() {
            // this.clearSelectedComponent();
            // this.setLayerSelected(true);
        },

        // 框选事件
        onFrameSelection(event) {
            let dx = event.pageX - this.frameSelectionDiv.startPageX
            let dy = event.pageY - this.frameSelectionDiv.startPageY
            //  Math.abs() 返回当前数值的绝对值
            this.frameSelectionDiv.width = Math.abs(dx)
            this.frameSelectionDiv.height = Math.abs(dy)
            if (dx > 0 && dy > 0) {
                this.frameSelectionDiv.top = this.frameSelectionDiv.startY
                this.frameSelectionDiv.left = this.frameSelectionDiv.startX
            } else if (dx > 0 && dy < 0) {
                this.frameSelectionDiv.top = this.frameSelectionDiv.startY + dy
                this.frameSelectionDiv.left = this.frameSelectionDiv.startX
            } else if (dx < 0 && dy > 0) {
                this.frameSelectionDiv.top = this.frameSelectionDiv.startY
                this.frameSelectionDiv.left = this.frameSelectionDiv.startX + dx
            } else if (dx < 0 && dy < 0) {
                this.frameSelectionDiv.top = this.frameSelectionDiv.startY + dy
                this.frameSelectionDiv.left = this.frameSelectionDiv.startX + dx
            }
            // 判断各个组件是否在方框内
            let _this = this
            let rect = {
                x: this.frameSelectionDiv.left,
                y: this.frameSelectionDiv.top,
                width: this.frameSelectionDiv.width,
                height: this.frameSelectionDiv.height
            }
            let components = this.configData.components
            components.forEach(component => {
                let itemRect = {
                    x: component.style.position.x,
                    y: component.style.position.y,
                    width: component.style.position.w,
                    height: component.style.position.h
                }
                if (checkByRectCollisionDetection(rect, itemRect)) {
                    _this.addSelectedComponent(component)
                } else {
                    _this.removeSelectedComponent(component)
                }
            })
            if (this.selectedComponents.length > 0) {
                this.setLayerSelected(false)
            } else {
                this.setLayerSelected(true)
            }
        },
        onDrop(event) {
            event.preventDefault()
            let infoJson = event.dataTransfer.getData('my-info')
            let component = JSON.parse(infoJson)
            if (this.checkAddComponent(component) === false) {
                return
            }
            // 判断当前着陆点是不是layer
            if (event.target.id === 'surface-edit-layer') {
                component.style.position.x = event.offsetX
                component.style.position.y = event.offsetY
            } else {
                // 解决着陆灯不是layer的情形
                let layer = event.currentTarget
                let position = layer.getBoundingClientRect()
                let x = event.clientX - position.left
                let y = event.clientY - position.top
                component.style.position.x = x
                component.style.position.y = y
            }
            this.execute({
                op: 'add',
                component: component
            })
            // 默认选中，并点击
            this.clickItem(component, this.configData.components.length - 1)
        },
        moveItems(direction) {
            let dx = 0
            let dy = 0
            if (direction === 'up') {
                dy = -5
            } else if (direction === 'right') {
                dx = 5
            } else if (direction === 'down') {
                dy = 5
            } else if (direction === 'left') {
                dx = -5
            }
            this.execute({
                op: 'move',
                dx: dx,
                dy: dy,
                items: this.selectedComponentMap
            })
        },
        checkAddComponent(info) {
            if (info == null) {
                this.$q.notify({
                    type: 'negative',
                    position: 'bottom-right',
                    message: 'This component not surpport.'
                })
                return false
            }
            return true
        },

        // 当前数据与tool组件类型匹配 返回一个组件名的字符串用于组件显示渲染
        parseView(component) {
            return ToPoMatch.parseViewName(component)
        },

        // 点击事件
        clickItem(component, index) {
            this.setLayerSelected(false)
            if (this.selectedComponentMap[component.identifier] === undefined) {
                this.setSelectedComponent(component)
            } else {
                // 如果已经选中，则不做任何处理
            }
        },
        clickComponent(index, component, event) {
            // 点击组件
            if (event.ctrlKey === true) {
                // 点击了ctrl
                this.setLayerSelected(false)
                if (this.selectedComponentMap[component.identifier] === undefined) {
                    this.addSelectedComponent(component)
                } else {
                    this.removeSelectedComponent(component)
                }
            } else {
                // 这里不再处理点击事件，改为鼠标左键
                // this.clickItem(component,index);
            }
        },
        copyItem() {
            // 设定复制源
            let items = []
            for (let key in this.selectedComponentMap) {
                let item = deepCopy(this.selectedComponentMap[key])
                items.push(item)
            }
            this.setCopySrcItems(items)
        },
        pasteItem() {
            if (this.copySrcItems && this.copySrcItems.length > 0) {
                this.execute({
                    op: 'copy-add',
                    items: this.copySrcItems
                })
            }
        },
        removeItem(index, component) {
            // 移除组件
            this.execute({
                op: 'del',
                index: index
            })
            this.setLayerSelected(true)
        },
        fullScreen() {
            localStorage.setItem('topoData', JSON.stringify(this.configData))
            let { href } = this.$router.resolve({
                path: '/fullscreen',
                name: 'TopoFullscreen',
                query: {
                    sceneId: this.sceneId,
                    sceneName: this.sceneName
                },
                params: {
                    sceneId: this.sceneId,
                    sceneName: this.sceneName
                }
            })
            window.open(href, '_blank')
        },
        printData() {
            let json = JSON.stringify(this.configData)
            console.log(json)
            alert(json)
        }
    }
}
</script>

<style lang="scss">
.el-card,
.el-card__body {
    width: 100%;
    height: 100%;
}
#topo-main-container {
    position: relative;
    width: 100%;
    height: 100%;

    // 回显区域
    .layer-container {
        position: relative;
        width: 100%;
        height: 100%;
        overflow: auto;
        // 画布
        .topo-layer {
            width: calc(100% - 20px);
            height: calc(100% - 20px);
            position: absolute;
            transform-origin: left top;
            overflow: auto;
            background-image: linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc),
                linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc);
            background-size: 20px 20px;
            background-position: 0 0, 10px 10px;

            .topo-frame-selection {
                background-color: #62627e;
                opacity: 0.4;
                border: #0000ff solid 1px;
                position: absolute;
                z-index: 9999999999;
            }

            .topo-layer-view {
                position: absolute;
                height: 100px;
                width: 100px;
                background-color: #999;
                cursor: move;
                border: #ccc solid 1px;

                .resize-left-top {
                    position: absolute;
                    height: 10px;
                    width: 10px;
                    background-color: white;
                    border: 1px solid rgb(255, 0, 0);
                    left: -5px;
                    top: -5px;
                    cursor: nwse-resize;
                }

                .resize-left-center {
                    position: absolute;
                    height: 10px;
                    width: 10px;
                    background-color: white;
                    border: 1px solid rgb(255, 0, 0);
                    left: -5px;
                    top: 50%;
                    margin-top: -5px;
                    cursor: ew-resize;
                }

                .resize-left-bottom {
                    position: absolute;
                    height: 10px;
                    width: 10px;
                    background-color: white;
                    border: 1px solid rgb(255, 0, 0);
                    left: -5px;
                    bottom: -5px;
                    cursor: nesw-resize;
                }

                .resize-right-top {
                    position: absolute;
                    height: 10px;
                    width: 10px;
                    background-color: white;
                    border: 1px solid rgb(255, 0, 0);
                    right: -5px;
                    top: -5px;
                    cursor: nesw-resize;
                }

                .resize-right-center {
                    position: absolute;
                    height: 10px;
                    width: 10px;
                    background-color: white;
                    border: 1px solid rgb(255, 0, 0);
                    right: -5px;
                    top: 50%;
                    margin-top: -5px;
                    cursor: ew-resize;
                }

                .resize-right-bottom {
                    position: absolute;
                    height: 10px;
                    width: 10px;
                    background-color: white;
                    border: 1px solid rgb(255, 0, 0);
                    right: -5px;
                    bottom: -5px;
                    cursor: nwse-resize;
                }

                .resize-center-top {
                    position: absolute;
                    height: 10px;
                    width: 10px;
                    background-color: white;
                    border: 1px solid rgb(255, 0, 0);
                    top: -5px;
                    left: 50%;
                    margin-left: -5px;
                    cursor: ns-resize;
                }

                .resize-center-bottom {
                    position: absolute;
                    height: 10px;
                    width: 10px;
                    background-color: white;
                    border: 1px solid rgb(255, 0, 0);
                    bottom: -5px;
                    left: 50%;
                    margin-left: -5px;
                    cursor: ns-resize;
                }
            }

            .topo-layer-view-selected {
                outline: 1px solid rgb(255, 0, 0);
            }
        }
    }
}
</style>
